//
//  CSHTTPRequestWork.m
//  文件上传的封装(工具类)基本POST请求
//
//  Created by 姚彦兆 on 15/10/18.
//  Copyright © 2015年 姚彦兆. All rights reserved.
//

#define kBoundary @"boundary"

#import "CSHTTPRequestWork.h"

@implementation CSHTTPRequestWork

/**
 *  上传多个文件并且附带普通文字参数的方法
 *
 *  @param requestURLString      请求的服务器的URL地址字符串
 *  @param fileMessageDictionary 待上传的文件信息字典,其中Key为服务器存储的文件名称,value为文件路径
 *  @param fileKey               服务器接收文件存储的关键字,由后台设置
 *  @param paramterDictionary    普通参数的信息字典,其中Key为服务器接受参数的关键字,value为参数内容
 *  @param completeQueue         完成后回调Block执行的线程
 *  @param uploadSuccess         上传请求成功后的回调Block
 *  @param uploadFailed          上传请求失败后的回调Block
 */
-(void)uploadMoreFilePOSTRequestWithURLString:(nonnull NSString *)requestURLString
                     andFileMessageDictionary:(nonnull NSDictionary *)fileMessageDictionary
                                   andFileKey:(nonnull NSString *)fileKey
                  andNormalParamterDictionary:(nonnull NSDictionary *)paramterDictionary
                             andCompleteQueue:(nonnull dispatch_queue_t)completeQueue
                              andSuccessBlock:(nonnull uploadMSuccessed)uploadSuccess
                                 andFailBlock:(nonnull failedBlock)uploadFailed{
    
    NSURL * requestURL = [NSURL URLWithString:requestURLString];
    
    //设置可变网络请求
    NSMutableURLRequest * customRequest = [NSMutableURLRequest requestWithURL:requestURL cachePolicy:0 timeoutInterval:15];
    
    //设置请求头信息
    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",kBoundary];
    
    [customRequest setValue:contentType forHTTPHeaderField:@"Content-Type"];
    
    customRequest.HTTPMethod = @"POST";
    
    //设置二进制Body信息
    NSData * httpBody = [self getHTTPBodyWithFileMessageDictionary:fileMessageDictionary andNormalParamaterDictionary:paramterDictionary andFilekey:fileKey] ;
    
    customRequest.HTTPBody = httpBody ;

    //向服务器发送请求
    [[[NSURLSession sharedSession] uploadTaskWithRequest:customRequest fromData:httpBody completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            
            if (data && !error) {
                
                NSLog(@"上传成功...");
                
                if (uploadSuccess) {
                    
                    id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
                    
                    uploadSuccess(result , response);
                    
                }
                
            }else{
                
                NSLog(@"上传失败...");
                
                if (uploadFailed) {
                    
                    uploadFailed(error);
                    
                }
                
            }
            
    }] resume];
    
}

/**
 *  自定义上传单文件的方法
 *
 *  @param requestURLString 上传文件请求的URL地址
 *  @param filePath         文件的路径(在本地需要上传的文件的路径)
 *  @param fileKey          服务器接受文件存储信息的Key值(存储文件类型 , 文件名称 , 文件大小 , 错误信息等)
 *  @param fileName         上传后保存在服务期端的文件名
 *  @param completeQueue    完成后的回调方法运行的线程
 *  @param successBlock     完成后回调(成功)
 *  @param failedBlock      完成后回调(失败)
 */
-(void)uploadFilePOSTRequestWithURLString:(nonnull NSString *)requestURLString
                            andFilePath:(nonnull NSString*)filePath
                             andFileKey:(nonnull NSString *)fileKey
                            andFileName:(NSString * _Nullable)fileName
                       andCompleteQueue:(nonnull dispatch_queue_t)completeQueue
                        andSuccessBlock:(nonnull successBlcok)successBlock
                           andFailBlock:(nonnull failedBlock)failedBlock{

    NSURL * requestURL = [NSURL URLWithString:requestURLString];
    
    NSMutableURLRequest * customRequest = [NSMutableURLRequest requestWithURL:requestURL cachePolicy:0 timeoutInterval:15];
    
    //设置请求头信息,告诉服务器我发送的请求
    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",kBoundary];
    [customRequest setValue:contentType forHTTPHeaderField:@"Content-Type"];
    
    customRequest.HTTPMethod = @"POST";
    
    //自定义方法返回HTTPBody
    NSData * httpBody = [self getHTTPBodyWithFilePath:filePath andServersFileKey:fileKey andFileNameToSaveOnServers:fileName];
    
    customRequest.HTTPBody = httpBody ;

    //使用Session的Upload方法对文件进行上传
    [[[NSURLSession sharedSession] uploadTaskWithRequest:customRequest fromData:httpBody completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        dispatch_async(completeQueue, ^{
            
            if (data && !error) {
                
                if (successBlock) {
                    //成功后(网页有响应)的返回Block
                    successBlock(data,response);
                }
            }else
            {
                if (failedBlock) {
                    //服务器无响应的Block
                    failedBlock(error);
                }
            }
            
        });
        
    }] resume];
    
//    [NSURLConnection sendAsynchronousRequest:customRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
//        
//        
//    }];

}

/**
 *  发送普通POST请求 传递HTML的参数
 *
 *  @param requestURL    请求的URL
 *  @param paramaterDic  传给服务器的参数
 *  @param completeQueue 完成后回调的Block执行的线程
 *  @param successBlock  完成后Block(成功)
 *  @param failedBlock   完成后Block(失败)
 */

-(void)sendPOSTRequestWithURLString:(nonnull NSString *)requestURLString
                       andParamater:(NSDictionary* _Nullable)paramaterDic
                   andCompleteQueue:(nonnull dispatch_queue_t)completeQueue
                    andSuccessBlock:(nonnull successBlcok)successBlock
                       andFailBlock:(nonnull failedBlock)failedBlock{

    //根据传入的字符串处理得到URL与请求(line)[32 , 34]
    NSURL * requestURL = [NSURL URLWithString:requestURLString];
    
    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:requestURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15];
    //设置请求方式为POST
    request.HTTPMethod = @"POST" ;
    
    if (paramaterDic) {
    
    //设置参数的容器
    __block NSMutableString * requestSTR = [NSMutableString stringWithFormat:@""];
    
    //遍历字典取出参数
    [paramaterDic enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
       
        NSString * seversKey = key;
        
        NSString * seversValue = obj;
        
        //拼接参数
        [requestSTR appendString:[NSString stringWithFormat:@"%@=%@&" , seversKey , seversValue]];
        
    }];
    
    //格式化参数
    requestSTR = (NSMutableString *)[requestSTR substringToIndex:(requestSTR.length - 1)] ;
    
    /**
     *  格式化参数方法2
     */
    //requestSTR = (NSMutableString *)[requestSTR stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"&"]];
    //二进制化字符串设置请求体
    NSData * parameterData = [requestSTR dataUsingEncoding:NSUTF8StringEncoding];
    
    request.HTTPBody = parameterData ;
    }
    //发送请求
    [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        dispatch_async(completeQueue, ^{
    
            if (data) {
                
                //请求成功有返回值
                if (successBlock) {
                    
                    //给自定义回调Block传递参数
                    successBlock(data , response) ;
                    
//                    NSLog(@"%@" , [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]);
                    
                }
                
            }else{
                
                //请求失败,网络连接问题
                if (failedBlock) {
                    
                    //给自定义回调Block传递参数
                    failedBlock(error);
                    
                }
                
            }
            
        });
    
    }] resume];

}

/**
 *  获得HTTPBody内容(多文件内容&参数)
 *
 *  @param fileMessageDictionary 文件信息字典,其中Key值为文件在服务器中存储的名称,value为文件在本地存储的路径,用来获取文件的二进制数据写入请求体
 *  @param paramaterDictionary   普通参数字典,其中的Key值为服务器接受文件的关键字,由后台管理;value值式是传入的内容
 *  @param fileKey               服务器接受上传文件的关键字,由后台管理
 */
-(nonnull NSData * )getHTTPBodyWithFileMessageDictionary:(nonnull NSDictionary *)fileMessageDictionary
                    andNormalParamaterDictionary:(nonnull NSDictionary *)paramaterDictionary
                                      andFilekey:(nonnull NSString *)fileKey{

    //设置二进制容器存储请求体信息
    NSMutableData * messageData = [NSMutableData data];
    
    //遍历文件信息字典获取文件的请求体信息
    [fileMessageDictionary enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
       
        //文件字典Key值:文件在服务器被保存的名称
        NSString * fileName = key;
        
        //文件字典Value值:文件在本地的存储路径
        NSString * filePath = obj;
        
        //封装请求头信息
        NSMutableString * headSTR = [NSMutableString stringWithFormat:@"\r\n--%@\r\n" , kBoundary];
        
        [headSTR appendString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n" , fileKey , fileName]];
        
        NSURLResponse * response = [self getFileTypeWithFilePath:filePath];
        
        NSString * fileType = response.MIMEType;
        
        [headSTR appendString:[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n" , fileType]];
        
        //将请求头信息放入二进制容器中
        [messageData appendData:[headSTR dataUsingEncoding:NSUTF8StringEncoding]];
        
        //将文件二进制数据放入二进制容器中
        [messageData appendData:[NSData dataWithContentsOfFile:filePath]];
        
    }];
    
    //遍历普通参数字典获取字符串的请求体信息
    
    [paramaterDictionary enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
       
        //普通参数字典Key值:表示服务器接受文本的Key值(由后台提供)
        NSString * seversKey = key;
        
        //普通参数字典Value值:表示上传的文本内容参数(由用户提供)
        NSString * userInput = obj;
        
//        -----------------------------199683830321344068521409122076 Content-Disposition: form-data; name="username"
        
        //格式化封装请求头信息
        NSMutableString * headSTRText = [NSMutableString stringWithFormat:@"\r\n--%@\r\n" , kBoundary];
        
        [headSTRText appendString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=%@\r\n\r\n" , seversKey]];
        
        //追加容器内容
        [messageData appendData:[headSTRText dataUsingEncoding:NSUTF8StringEncoding]];
        
        [messageData appendData:[userInput dataUsingEncoding:NSUTF8StringEncoding]];
        
    }];
    
    //添加下边界,只需添加一次
    NSMutableString *footerStrM = [NSMutableString stringWithFormat:@"\r\n--%@--\r\n",kBoundary];
    
    [messageData appendData:[footerStrM dataUsingEncoding:NSUTF8StringEncoding]];
    
    return messageData;

}

/**
 *  获得HTTPBody内容
 *
 *  @param filePath 本地文件的路径,用于获得本地文件的二进制数据
 *  @param fileKey  服务器(后台人员)提供的
 *  @param fileName 上传后的文件存储的名字
 *
 *  @return 返回待上传文件的HTTPBody
 */

-(NSData *)getHTTPBodyWithFilePath:(nonnull NSString *)filePath
                 andServersFileKey:(nonnull NSString *)fileKey
        andFileNameToSaveOnServers:(NSString * _Nullable )fileName{
    
    //获得动态的文件类型来设置Content-Type
    NSURLResponse * response = [self getFileTypeWithFilePath:filePath];
    
    NSLog(@"%@ , %lld" , response.MIMEType , response.expectedContentLength);
    
    NSString * fileType = response.MIMEType;
    
    if (!fileName) {
        
        fileName = response.suggestedFilename;
        
    }
    
    //定义可变的二进制数据用来存储待上传文件的数据
    NSMutableData * data = [NSMutableData data];
    
#pragma mark ----拼接请求头字符串
    
    //请求头标准格式
    NSMutableString * headSTR = [NSMutableString stringWithFormat:@"\r\n--%@\r\n" , kBoundary];
    
    //请求体中的数据,fileKey由服务器提供,fileName由用户(程序员)[前端]决定 可为空
    [headSTR appendString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n" , fileKey , fileName]];
    
    //根据自定义方法返回的文件信息设置文件格式
    [headSTR appendString:[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n" , fileType]] ;
    
    NSLog(@"headSTR : %@" , headSTR);
    
#pragma mark ----拼接二进制数据
    
    //获得等待上传文件的二进制数据
    [data appendData:[headSTR dataUsingEncoding:NSUTF8StringEncoding]];
    
    NSData * fileData = [NSData dataWithContentsOfFile:filePath];
    
    //拼接二进制数据
    [data appendData:fileData];
    
#pragma mark ----拼接请求尾字符串
    
    //请求尾标准格式
    NSString * tailSTR = [NSString stringWithFormat:@"\r\n--%@--" , kBoundary];
    
    NSLog(@"tailSTR : %@" , tailSTR);
    
    [data appendData:[tailSTR dataUsingEncoding:NSUTF8StringEncoding]];
    
    return data ;

}

/**
 *  根据[用户或者程序员]传入的文件路径来动态获取文件类型
 *
 *  @param filePath 传入的需要获得类型的文件路径
 *
 *  @return 返回一个NSURLResponse对象,里面包含着系统返回的文件信息
 */
-(NSURLResponse *)getFileTypeWithFilePath:(nonnull NSString *)filePath{

    //定义文件的返回值
    __block NSURLResponse * fileResponse = nil;
    
    //获取文件字符串--本地
    NSString * fileSTR = [NSString stringWithFormat:@"file://%@" , filePath];

    NSURL * fileURL = [NSURL URLWithString:fileSTR];
    
    //设置请求--请求本地文件数据
    NSMutableURLRequest * customRequest = [NSMutableURLRequest requestWithURL:fileURL cachePolicy:0 timeoutInterval:15];
    
    /**
     *   过期的同步方法
     */
    [NSURLConnection sendSynchronousRequest:customRequest returningResponse:&fileResponse error:NULL];
    
      //设置同步
    
//    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
//    
//    dispatch_sync(serialQueue, ^{
//        
//        customRequest.HTTPMethod = @"POST";
//        
//        [[[NSURLSession sharedSession] dataTaskWithRequest:customRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//            
//            NSLog(@"%@",[NSThread currentThread]);
//            
//            NSLog(@"response %@ " , response.MIMEType);
//            
//            fileResponse = response ;
//            
//            NSLog(@"file %@" , fileResponse.MIMEType);
//            
//        }] resume];
//        
//    });
 
    return fileResponse ;
    
}




+(instancetype)sharedCSHTTPRequestWork{

    __block id _instance ;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        _instance = [[CSHTTPRequestWork alloc]init];
        
    });

    return _instance ;
    
}

@end
